package heap

import (
	"fmt"
	"sync"

	"hundsun.com/hsl/hschain/contract/jvm/jvm.go/classpath"
	"hundsun.com/hsl/hschain/contract/jvm/jvm.go/vmutils"
)

type Runtime struct {
	bootLoader     *ClassLoader
	stringPool     map[string]*Object // interned strings
	stringPoolLock sync.RWMutex
}

func NewThreadRuntime(cp *classpath.ClassPath, verboseClass bool, jvmbl *ClassLoader) *Runtime {
	bl := &ClassLoader{
		classPath: cp,
		classMap:  make(map[string]*Class),
		verbose:   false,
	}
	rt := &Runtime{
		bootLoader:     bl,
		stringPool:     map[string]*Object{},
		stringPoolLock: sync.RWMutex{},
	}
	bl.rt = rt
	//bl.CopyBootloader(jvmbl)
	//bl.baseCode = jvmbl.baseCode
	bl.init()
	return rt
}
func NewRuntime(cp *classpath.ClassPath, verboseClass bool, jvmbl *ClassLoader) *Runtime {
	rt := &Runtime{
		bootLoader: jvmbl,
		stringPool: map[string]*Object{},
	}
	jvmbl.rt = rt
	return rt
}

func (rt *Runtime) BootLoader() *ClassLoader {
	return rt.bootLoader
}

/* string pool */

func (rt *Runtime) JSIntern(goStr string, jStr *Object) *Object {
	internedStr := rt.JsGet(goStr)
	if internedStr != nil {
		return internedStr
	}
	rt.JsSet(goStr, jStr)
	return jStr
}

func (rt *Runtime) JsGet(goStr string) *Object {
	rt.stringPoolLock.RLock()
	defer rt.stringPoolLock.RUnlock()
	if internedStr, ok := rt.stringPool[goStr]; ok {
		return internedStr
	}
	return nil
}

func (rt *Runtime) JsSet(goStr string, jStr *Object) {
	rt.stringPoolLock.Lock()
	defer rt.stringPoolLock.Unlock()
	rt.stringPool[goStr] = jStr
}

// go string -> java.lang.String
func (rt *Runtime) JSFromGoStr(goStr string) *Object {
	internedStr := rt.JsGet(goStr)
	if internedStr != nil {
		return internedStr
	}
	chars := vmutils.UTF8ToUTF16(goStr)
	jStr := rt.JSFromChars(chars)
	jStr = rt.JSIntern(goStr, jStr) // TODO
	return jStr
}
func (rt *Runtime) JSFromGoStrNoIntern(goStr string) *Object {
	chars := vmutils.UTF8ToUTF16(goStr)
	jStr := rt.JSFromChars(chars)
	return jStr
}

// java char[] -> java.lang.String
func (rt *Runtime) JSFromChars(chars []uint16) *Object {
	charArr := rt.NewCharArray(chars)
	jStr := rt.bootLoader.JLStringClass().NewObj()
	jStr.SetFieldValue("value", "[C", NewRefSlot(charArr))
	return jStr
}

/* array factory */

func (rt *Runtime) NewByteArray(bytes []int8) *Object {
	return newObj(rt.bootLoader.getClass("[B"), bytes, nil)
}
func (rt *Runtime) NewCharArray(chars []uint16) *Object {
	return newObj(rt.bootLoader.getClass("[C"), chars, nil)
}
func (rt *Runtime) NewObjectArray(objs []*Object) *Object {
	return newObj(rt.bootLoader.jlObjectClass.getArrayClass(), objs, nil)
}
func (rt *Runtime) NewStringArray(objs []*Object) *Object {
	return newObj(rt.bootLoader.jlStringClass.getArrayClass(), objs, nil)
}
func (rt *Runtime) NewClassArray(objs []*Object) *Object {
	return newObj(rt.bootLoader.jlClassClass.getArrayClass(), objs, nil)
}

func (rt *Runtime) NewPrimitiveArray(atype uint8, count uint) *Object {
	switch atype {
	case ATBoolean:
		return newPrimitiveArray(rt.bootLoader.getClass("[Z"), count)
	case ATByte:
		return newPrimitiveArray(rt.bootLoader.getClass("[B"), count)
	case ATChar:
		return newPrimitiveArray(rt.bootLoader.getClass("[C"), count)
	case ATShort:
		return newPrimitiveArray(rt.bootLoader.getClass("[S"), count)
	case ATInt:
		return newPrimitiveArray(rt.bootLoader.getClass("[I"), count)
	case ATLong:
		return newPrimitiveArray(rt.bootLoader.getClass("[J"), count)
	case ATFloat:
		return newPrimitiveArray(rt.bootLoader.getClass("[F"), count)
	case ATDouble:
		return newPrimitiveArray(rt.bootLoader.getClass("[D"), count)
	default:
		panic(fmt.Errorf("invalid atype: %v", atype))
	}
}
